 /*******************************************************************************
  * Copyright (c) 2005, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal;

 import org.eclipse.core.runtime.Assert;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IEditorReference;
 import org.eclipse.ui.IPropertyListener;
 import org.eclipse.ui.ISaveablesLifecycleListener;
 import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.IWorkbenchPartConstants;
 import org.eclipse.ui.IWorkbenchPartReference;
 import org.eclipse.ui.IWorkbenchPartSite;
 import org.eclipse.ui.part.MultiEditor;

 public abstract class PartList {
     private IWorkbenchPartReference activePartReference;

     private IEditorReference activeEditorReference;

     // private List parts = new ArrayList();

     private IPropertyListener listener = new IPropertyListener() {
         public void propertyChanged(Object source, int propId) {
             WorkbenchPartReference ref = (WorkbenchPartReference) source;

             switch (propId) {
             case WorkbenchPartReference.INTERNAL_PROPERTY_OPENED:
                 partOpened(ref);
                 break;
             case WorkbenchPartReference.INTERNAL_PROPERTY_CLOSED:
                 partClosed(ref);
                 break;
             case WorkbenchPartReference.INTERNAL_PROPERTY_VISIBLE: {
                 if (ref.getVisible()) {
                     partVisible(ref);
                 } else {
                     partHidden(ref);
                 }
                 break;
             }
             case IWorkbenchPartConstants.PROP_INPUT: {
                 partInputChanged(ref);
                 break;
             }
             }
         }
     };

     public IWorkbenchPartReference getActivePartReference() {
         return activePartReference;
     }

     public IEditorReference getActiveEditorReference() {
         return activeEditorReference;
     }

     public IEditorPart getActiveEditor() {
         return activeEditorReference == null ? null : activeEditorReference
                 .getEditor(false);
     }

     public IWorkbenchPart getActivePart() {
         return activePartReference == null ? null : activePartReference
                 .getPart(false);
     }

     public void addPart(WorkbenchPartReference ref) {
         Assert.isNotNull(ref);

         ref.addInternalPropertyListener(listener);

         // parts.add(ref);
 firePartAdded(ref);

         // If this part is already open, fire the "part opened" event
 // immediately
 if (ref.getPart(false) != null) {
             partOpened(ref);
         }

         // If this part is already visible, fire the visibility event
 // immediately
 if (ref.getVisible()) {
             partVisible(ref);
         }
     }

     /**
      * Sets the active part.
      *
      * @param ref
      */
     public void setActivePart(IWorkbenchPartReference ref) {
         if (ref == activePartReference) {
             return;
         }

         IWorkbenchPartReference oldPart = activePartReference;

         // A part can't be activated until it is added
 // Assert.isTrue(ref == null || parts.contains(ref));

         if (ref != null) {
             IWorkbenchPart part = ref.getPart(true);
             Assert.isNotNull(part);
             if (part instanceof MultiEditor) {
                 IWorkbenchPartSite site = ((MultiEditor) part)
                         .getActiveEditor().getSite();
                 if (site instanceof PartSite) {
                     ref = ((PartSite) site).getPane().getPartReference();
                 }
             }
         }

         activePartReference = ref;

         fireActivePartChanged(oldPart, ref);
     }

     public void setActiveEditor(IEditorReference ref) {
         if (ref == activeEditorReference) {
             return;
         }

         // A part can't be activated until it is added
 // Assert.isTrue(ref == null || parts.contains(ref));

         if (ref != null) {
             IWorkbenchPart part = ref.getPart(true);
             Assert.isNotNull(part);
             if (part instanceof MultiEditor) {
                 IWorkbenchPartSite site = ((MultiEditor) part)
                         .getActiveEditor().getSite();
                 if (site instanceof PartSite) {
                     ref = (IEditorReference) ((PartSite) site).getPane()
                             .getPartReference();
                 }
             }
         }

         activeEditorReference = ref;

         fireActiveEditorChanged(ref);
     }

     /**
      * In order to remove a part, it must first be deactivated.
      */
     public void removePart(WorkbenchPartReference ref) {
         Assert.isNotNull(ref);
         // It is an error to remove a part that isn't in the list
 // Assert.isTrue(parts.contains(ref));
 // We're not allowed to remove the active part. We must deactivate it
 // first.
 Assert.isTrue(ref != activePartReference);
         // We're not allowed to remove the active editor. We must deactivate it
 // first.
 Assert.isTrue(ref != activeEditorReference);

         if (ref.getVisible()) {
             ref.setVisible(false);
         }

         // If this part is currently open, fire the "part closed" event before
 // removal
 if (ref.getPart(false) != null) {
             partClosed(ref);
         }

         ref.removeInternalPropertyListener(listener);

         firePartRemoved(ref);
     }

     private void partInputChanged(WorkbenchPartReference ref) {
         firePartInputChanged(ref);
     }

     private void partHidden(WorkbenchPartReference ref) {
         // Part should not be null
 Assert.isNotNull(ref);
         // This event should only be fired if the part is actually visible
 Assert.isTrue(!ref.getVisible());
         // We shouldn't be receiving events from parts until they are in the
 // list
 // Assert.isTrue(parts.contains(ref));

         firePartHidden(ref);
     }

     private void partOpened(WorkbenchPartReference ref) {
         Assert.isNotNull(ref);

         IWorkbenchPart actualPart = ref.getPart(false);
         // We're firing the event that says "the part was just created"... so
 // there better be a part there.
 Assert.isNotNull(actualPart);
         // Must be called after the part is inserted into the part list
 // Assert.isTrue(parts.contains(ref));
 // The active part must be opened before it is activated, so we should
 // never get an
 // open event for a part that is already active. (This either indicates
 // that a redundant
 // open event was fired or that a closed part was somehow activated)
 Assert.isTrue(activePartReference != ref);
         // The active editor must be opened before it is activated, so we should
 // never get an
 // open event for an editor that is already active. (This either
 // indicates that a redundant
 // open event was fired or that a closed editor was somehow activated)
 Assert.isTrue(activeEditorReference != ref);

         SaveablesList modelManager = (SaveablesList) actualPart
                 .getSite().getService(ISaveablesLifecycleListener.class);
         modelManager.postOpen(actualPart);

         // Fire the "part opened" event
 firePartOpened(ref);
     }

     /**
      * Called when a concrete part is about to be destroyed. This is called
      * BEFORE disposal happens, so the part should still be accessable from the
      * part reference.
      *
      * @param ref
      */
     private void partClosed(WorkbenchPartReference ref) {
         Assert.isNotNull(ref);

         IWorkbenchPart actualPart = ref.getPart(false);
         // Called before the part is disposed, so the part should still be
 // there.
 Assert.isNotNull(actualPart);
         // Must be called before the part is actually removed from the part list
 // Assert.isTrue(parts.contains(ref));
 // Not allowed to close the active part. The part must be deactivated
 // before it may
 // be closed.
 Assert.isTrue(activePartReference != ref);
         // Not allowed to close the active editor. The editor must be
 // deactivated before it may
 // be closed.
 Assert.isTrue(activeEditorReference != ref);

         firePartClosed(ref);
     }

     private void partVisible(WorkbenchPartReference ref) {
         // Part should not be null
 Assert.isNotNull(ref);
         // This event should only be fired if the part is actually visible
 Assert.isTrue(ref.getVisible());
         // We shouldn't be receiving events from parts until they are in the
 // list
 // Assert.isTrue(parts.contains(ref));
 // Part must be open before it can be made visible
 Assert.isNotNull(ref.getPart(false));

         firePartVisible(ref);
     }

     /**
      * Fire the event indicating that a part reference was just realized. That
      * is, the concrete IWorkbenchPart has been attached to the part reference.
      *
      * @param part
      * the reference that was create
      */
     protected abstract void firePartOpened(IWorkbenchPartReference part);

     /**
      * Fire the event indicating that a part reference was just realized. That
      * is, the concrete IWorkbenchPart has been attached to the part reference.
      *
      * @param part
      * the reference that was create
      */
     protected abstract void firePartClosed(IWorkbenchPartReference part);

     /**
      * Indicates that a new part reference was added to the list.
      *
      * @param part
      */
     protected abstract void firePartAdded(IWorkbenchPartReference part);

     /**
      * Indicates that a part reference was removed from the list
      *
      * @param part
      */
     protected abstract void firePartRemoved(IWorkbenchPartReference part);

     /**
      * Indicates that the active editor changed
      *
      * @param part
      * active part reference or null if none
      */
     protected abstract void fireActiveEditorChanged(IWorkbenchPartReference ref);

     /**
      * Indicates that the active part has changed
      *
      * @param part
      * active part reference or null if none
      */
     protected abstract void fireActivePartChanged(
             IWorkbenchPartReference oldPart, IWorkbenchPartReference newPart);

     /**
      * Indicates that the part has been made visible
      *
      * @param ref
      */
     protected abstract void firePartVisible(IWorkbenchPartReference ref);

     /**
      * Indicates that the part has been hidden
      *
      * @param ref
      */
     protected abstract void firePartHidden(IWorkbenchPartReference ref);

     /**
      * Indicates that the part input has changed
      *
      * @param ref
      */
     protected abstract void firePartInputChanged(IWorkbenchPartReference ref);

     protected abstract void firePartBroughtToTop(IWorkbenchPartReference ref);
 }

